一、装饰器decorator的作用与组成:**
作用:在不修改被修饰函数主体及调用方式的前提下,为被修饰函数提供新的功能
组成:内嵌函数+高阶函数+闭包=》装饰器
内嵌(部)函数:
定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
例如:
1 | >>> |
注释掉level3中的num赋值,则num继承上一级level2作用域中的值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
171 num=
def level1():
2 num=
'level1') print(num,
def level2():
3 num=
'level2') print(num,
def level3():
#num=4
'level3') print(num,
level3()
level2()
...
level1()
2 level1
3 level2
3 level3
注释掉level2、level3中的num赋值,则level2、level3中num均继承level1作用域中的值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
181 num=
def level1():
2 num=
'level1') print(num,
def level2():
#num=3
'level2') print(num,
def level3():
#num=4
'level3') print(num,
level3()
level2()
...
level1()
2 level1
2 level2
2 level3
>>>
总结:变量在函数中的作用域只能正向继承,但层级越近优先级越高,本地优先级最高,全局优先级最低。
高阶函数:
满足下列条件之一的函数,即可被称之为高阶函数:
1.某一函数当做参数传入另一个函数中
2.函数的返回值包含n个函数,n>0
例如:1
2
3
4
5
6
7
8
9
10
11
12def foo(func): ####stage2
#####stage3 res=func()
return res ####stage4
...
def calc():
for i in range(3)]) print([i
return 'run end'
...
>>>
####stage1 foo(calc)
[0, 1, 2]
'run end'
执行过程:
stage1.将calc函数主体这个对象,calc作为实参传递给foo,替代形参func
stage2:运行foo函数
stage3:将calc()赋值给变量res,并开始执行calc()函数,
stage4:将clac()的函数返回值传递给res
好像并没有什么X用,绕了一圈不如直接调用calc(),那么接着往下
闭包
python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行了引用,并且函数返回值也是内部函数本身,那么该内部函数就被认为是闭包(closure)。1
2
3
4
5
6
7
8def outside _func(para1):
def inside_func(para2):
return para1*para2
return inside_func # 将inside_func作为outside_func的返回值
a = outside_func(12) # 此时 para1=12,执行outside_func(12)
print(a) #此时的inside_func并未赋予参数被执行,因此上方执行的结果只能是一段inside_func的内存指针
print(a(21)) # para2=21,等价于print(outside_func(12)(21))
此时的inside_func就叫做闭包,闭包的作用:
1.隐藏内部函数,只暴露外部函数
2.如果内部函数使用了外部函数的变量,则此外部变量不会随着外部函数一起销毁,而会一直保存在内存中
二、内嵌函数+高阶函数+闭包=》装饰器
假设要给函数增加一个记录运行时间的功能,要求不改变函数主体和调用方式:
**
1 | import time |
过程解析:
stage1:将test1函数作为实参传递给timmer函数,运算得出的结果的内存指针再重新指向给test1
stage2:开始执行test1(读取test1内存指针并开始运算)
stage3:执行timmer(func),即timmer(test1)
stage4:执行warpper,记录当前时间赋值给start_time
stage5:执行func(),即test1()函数
stage6:test1()执行完成后记录当前时间,赋值给stop_time
stage7:warpper的执行结果return给timmer,执行全部完成。
ps:stage1中的test1=timmer(test1)看起来比够简洁,在python中有语法糖这么一个存在,@符号表示,test1=timmer(test)等价于在定义test1的上方@timmer
**
函数在执行过程中可能会带有参数,那么带参数的函数如何配合装饰器中使用呢?改良版:
**
1 | import time |
函数可能会手动定义返回值,例如上方定义了return ‘run end’,但是经过装饰器包装了之后现在并没有返回值,如何保留函数原本的返回值呢?进阶版:
1 | import time |
在闭包函数内最后一行添加一个return func()即可返回func()函数原本的return返回值
若此时要求实现较为复杂的逻辑,装饰器本身也要携带不同的参数以实现多元化的功能。例如在上方例子基础上,要求增加在不同的板块内计时的倍率不同的功能。高级版:
1 | import time |
相比上方版本,只是在原本两层的装饰器外再添加了一层,可以用来接收装饰器本身的参数,在装饰器内层加以判断,以实现差异化的功能,最终在test1内执行4s和test2内执行2s,计时结果时间接近一致。